#include <boost/numeric/conversion/cast.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>

#include <string>
#include <fstream>
#include <iostream>
#include <stdio.h>

#include <OgreTagPoint.h>
#include <OgreBone.h>
#include <Ogre.h>

#include "UI.h"
#include "InputManager.h"
// Lua
#include <tolua++.h>
#include <lua.hpp>
#include <lualib.h>
#include <lauxlib.h>
#include "tolua_Vector3.h"
#include "tolua_ColourValue.h"
#include "tolua_Barrage.h"
#include "tolua_Bullet.h"

// CEGUI includes
#include <CEGUI.h>
#include <ScriptingModules/LuaScriptModule/CEGUILua.h>
// system includes
#include "STGEngine.h"
#include "Barrage.h"
#include "Player.h"
#include "STGObjectManager.h"
#include "STGLevel.h"
//-------------------------------------------------------------------------------------
using namespace Ogre;
using namespace boost;
namespace fs = boost::filesystem;

CSTGEngine::CSTGEngine()
: ogreHead(0),
headNode(0)
, mPlayerInst(NULL)
{
    // Set default values for variables
    mFolder = "D:/projects/stg";

    //freopen("cout2.txt", "w", stdout);
    std::filebuf *f = new std::filebuf;
    f->open("cout.txt", std::ios::out);
    std::streambuf* buf = std::cout.rdbuf();
    std::cout.rdbuf(f);
    std::cout << "Hello File!" <<std::endl;
    //std::cout.rdbuf(buf);
}
//-------------------------------------------------------------------------------------
CSTGEngine::~CSTGEngine()
{
}
//-------------------------------------------------------------------------------------
void CSTGEngine::addResourceLocations() {
    CEngine::addResourceLocations();
    
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
        (fs::path(mFolder)/"resource").string().c_str(), "FileSystem", "Magica");
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
        (fs::path(mFolder)/"resource/image").string().c_str(), "FileSystem", "Magica");
    //===========================================================
    // CEGUI
    //Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
    //    (fs::path(mFolder)/"resource/gui").string(), "FileSystem", "GUI");
#if defined(WIN32)
    Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
        "c:\\windows\\fonts", "FileSystem", "GUI");
#endif

    Ogre::ResourceGroupManager& resourceManager = Ogre::ResourceGroupManager::getSingleton();
    // add resource groups that we use
    resourceManager.createResourceGroup("imagesets");
    resourceManager.createResourceGroup("fonts");
    resourceManager.createResourceGroup("layouts");
    resourceManager.createResourceGroup("schemes");
    resourceManager.createResourceGroup("looknfeel");
    resourceManager.createResourceGroup("lua_scripts");
    resourceManager.createResourceGroup("schemas");

    resourceManager.addResourceLocation( (fs::path(mFolder)/"resource/gui/imagesets").string(), "FileSystem", "imagesets");
    resourceManager.addResourceLocation( (fs::path(mFolder)/"resource/gui/fonts").string(), "FileSystem", "fonts");
    resourceManager.addResourceLocation( (fs::path(mFolder)/"resource/gui/layouts").string(), "FileSystem", "layouts");
    resourceManager.addResourceLocation( (fs::path(mFolder)/"resource/gui/schemes").string(), "FileSystem", "schemes");
    resourceManager.addResourceLocation( (fs::path(mFolder)/"resource/gui/looknfeel").string(), "FileSystem", "looknfeel");
    resourceManager.addResourceLocation( (fs::path(mFolder)/"resource/gui/lua_scripts").string(), "FileSystem", "lua_scripts");

    //Create a resource Provider for the Taharez look'n'feel to be accesible
    CEGUI::Imageset::setDefaultResourceGroup("imagesets");
    CEGUI::Font::setDefaultResourceGroup("fonts");
    CEGUI::Scheme::setDefaultResourceGroup("schemes");
    CEGUI::WidgetLookManager::setDefaultResourceGroup("looknfeel");
    CEGUI::WindowManager::setDefaultResourceGroup("layouts");
    CEGUI::ScriptModule::setDefaultResourceGroup("lua_scripts");
}
//-------------------------------------------------------------------------------------
bool CSTGEngine::processUnbufferedKeyInput(const Ogre::FrameEvent& evt)
{
    using namespace Ogre;
    using namespace OIS;
    using Ogre::Vector3;
    OIS::Keyboard *mInputDevice = CInputManager::getSingletonPtr()->getKeyboard();
    Vector3 direction(Vector3::ZERO);
    if (mInputDevice->isKeyDown(KC_A)) {
        direction += Vector3(-1,0,0);
    }
    if (mInputDevice->isKeyDown(KC_D)) {
        direction += Vector3( 1,0,0);
    }
    if (mInputDevice->isKeyDown(KC_W)) {
        direction += Vector3( 0,1,0);
    }
    if (mInputDevice->isKeyDown(KC_S)) {
        direction += Vector3( 0,-1,0);
    }
    Real norm = direction.length();
    if (norm > 0.5) {
        direction = direction / norm;
        if (mPlayerInst) {
            mPlayerInst->move(direction, evt.timeSinceLastFrame);
        }
    }
    
    //if (mInputDevice->isKeyDown(KC_A))
    //{
    //    // Move camera left
    //    mTranslateVector.x = -mMoveScale;
    //}

    //if (mInputDevice->isKeyDown(KC_D))
    //{
    //    // Move camera RIGHT
    //    mTranslateVector.x = mMoveScale;
    //}

    ///* Move camera forward by keypress. */
    //if (mInputDevice->isKeyDown(KC_UP) || mInputDevice->isKeyDown(KC_W) )
    //{
    //    mTranslateVector.z = -mMoveScale;
    //}

    ///* Move camera backward by keypress. */
    //if (mInputDevice->isKeyDown(KC_DOWN) || mInputDevice->isKeyDown(KC_S) )
    //{
    //    mTranslateVector.z = mMoveScale;
    //}

    //if (mInputDevice->isKeyDown(KC_PGUP) || mInputDevice->isKeyDown(KC_E))
    //{
    //    // Move camera up
    //    mTranslateVector.y = mMoveScale;
    //}

    //if (mInputDevice->isKeyDown(KC_PGDOWN)|| mInputDevice->isKeyDown(KC_Q))
    //{
    //    // Move camera down
    //    mTranslateVector.y = -mMoveScale;
    //}

    //if (mInputDevice->isKeyDown(KC_RIGHT))
    //{
    //    mCamera->yaw(-mRotScale);
    //}

    //if (mInputDevice->isKeyDown(KC_LEFT))
    //{
    //    mCamera->yaw(mRotScale);
    //}

    if( mInputDevice->isKeyDown( KC_ESCAPE) )
    {
        return false;
    }

    //if (mInputDevice->isKeyDown(KC_F) && mTimeUntilNextToggle <= 0)
    //{
    //    mStatsOn = !mStatsOn;
    //    showDebugOverlay(mStatsOn);

    //    mTimeUntilNextToggle = 1;
    //}

    return true;
}
//-------------------------------------------------------------------------------------
bool CSTGEngine::processUnbufferedMouseInput(const Ogre::FrameEvent& evt)
{
    using namespace Ogre;
    using namespace OIS;

    return true;
}
void CSTGEngine::initLua() {
    mLua = lua_open();
    luaL_openlibs(mLua);   // initalize all lua standard library functions
    tolua_open(mLua);          // initalize tolua
    tolua_CBarrage_open(mLua);
}
void CSTGEngine::destroyLua() {
    lua_close(mLua);
}
class CBarrage* CSTGEngine::createBarrage( const std::string &type )
{
    //Ogre::SceneManager *sceneMgr = CSTGEngine::getSingletonPtr()->getSceneManager();
    static fs::path clipFolder = fs::path(mFolder)/"resource/barrage";
    char barrageName[256];
    sprintf(barrageName, "%s/%s.lua", clipFolder.string().c_str(), type.c_str());
    lua_State *L = lua();

    CBarrage *barrage = NULL;
    barrage = static_cast<CBarrage*>(
        mSceneMgr->createMovableObject("Barrage", CBarrageFactory::FACTORY_TYPE_NAME, NULL));
    mSceneMgr->getRootSceneNode()->createChildSceneNode("")->attachObject(barrage);

    barrage->_notifyCurrentCamera(mCamera);

    tolua_pushusertype(L, barrage, "CBarrage");
    lua_setglobal(L, "barrage");

    if (luaL_dofile(L, barrageName)) {
        LUA_ERROR(L);
    }

    barrage->setBillboardSetBounds(AxisAlignedBox(-1000,-1000,-100,1000,1000,100));
    barrage->configureRenderer();
    barrage->mIsEmitting = false;
    return barrage;
}
bool test(const CEGUI::EventArgs& ) {
    GAME_LOG("cegui click test");
    return true;
}
//-------------------------------------------------------------------------------------
void CSTGEngine::createScene()
{
    using namespace Ogre;
	ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");

	headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	headNode->attachObject(ogreHead);
    headNode->setVisible(false);
	// Set ambient light
	mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

	// Create a light
	Light* l = mSceneMgr->createLight("MainLight");
	l->setPosition(20,80,50);


    //// Set the camera to look at our handiwork
    mCamera->setPosition(0, 0,1000);
    mCamera->lookAt(Vector3(0,0,0));
    //mCamera->setProjectionType(PT_ORTHOGRAPHIC);
    //mCamera->setOrthoWindow(mWindow->getWidth(), mWindow->getHeight());
    mCamera->setNearClipDistance(0.1f);

    // setup debug particle    
    //// create some nice fireworks and place it at the origin

    //ParticleSystem* ps;
    //ps = mSceneMgr->createParticleSystem("Fireworks", "Magica/Fireworks");
    //ps = mSceneMgr->createParticleSystem("Fireworks", "Magica");

	//mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ps);

    if (CBarrageFactory::getSingletonPtr() == NULL) {
        CBarrageFactory *pMgr = new CBarrageFactory;
        Root::getSingletonPtr()->addMovableObjectFactory(pMgr);
    }
    //
    ////===========================================================
    //// lua 
    initLua();
    lua_State *L = lua();
    std::cout << "LogTest" << std::endl;
    if (luaL_dostring(lua(), "vec3 = Ogre.Vector3:new(); vec3.x = 0; vec3.y = 1; vec3.z = 0; \
                             io.stdout:write(\"vector3(\"..vec3.x..\", \"..vec3.y..\", \"..vec3.z..\")\\n\");")) {
                                 //lua_error(L);

                                 Ogre::LogManager::getSingletonPtr()->logMessage(lua_tostring(lua(), -1));
                                 lua_pop(lua(), 1); // remove error message
    }
    lua_getglobal(lua(), "vec3");
    Vector3 *vec3 = (Vector3*)tolua_tousertype(lua(), -1, 0);

    ////==============================================================================
    //// game object test

    if (CSTGObjectManager::getSingletonPtr() == NULL) {
        new CSTGObjectManager();
    }
    
    CSTGObjectManager::getSingletonPtr()->loadAll();
    
    CSTGLevel *level = new CSTGLevel;
    level->setBackground("bg_test.jpg");

    mPlayer = new CPlayer("player", "player_default.lua");

    fs::path path_old = fs::current_path();
    fs::current_path(fs::path(mFolder.c_str())/"resource/character");
    mPlayer->loadConfigScript();
    fs::current_path(path_old);

    mPlayerInst = static_cast<CPlayerInst*>(mPlayer->createInstance("Player1"));
    mPlayerInst->setPosition(Vector3(100,100,0));


    CWeaponInst *pWeapon = dynamic_cast<CWeaponInst*>(CSTGObjectManager::getSingletonPtr()->createWeaponInst("default", "main_weapon"));
    mPlayerInst->addWeapon(pWeapon, 0);
 
    //==============================================================================
    //  GUI test
    if (UI::getSingletonPtr() == NULL) {
        new UI;
    }
    UI::getSingletonPtr()->init(mWindow);
    mInputMgr->addMouseListener(UI::getSingletonPtr(), "CEGUI");
    mInputMgr->addKeyListener(UI::getSingletonPtr(), "CEGUI");


    using namespace CEGUI;
    //CEGUI::Window *myImageWindow = CEGUI::WindowManager::getSingleton().
    //    createWindow("TaharezLook/StaticImage","PrettyWindow" );
    //myImageWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.5,0),CEGUI::UDim(0.5,0)));
    //myImageWindow->setSize(UVector2(UDim(0,150),UDim(0,100)));
    //myImageWindow->setProperty("Image","set:TaharezLook image:full_image");
    //CEGUI::System::getSingletonPtr()->getGUISheet()->addChildWindow(myImageWindow);

    //CEGUI::Window * gJumpBtnWindow = CEGUI::WindowManager::getSingleton().
    //    createWindow("TaharezLook/Button","JumpPushButton");  // Create Window

    //gJumpBtnWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.75,0),CEGUI::UDim(0.50,0)));
    //gJumpBtnWindow->setSize(CEGUI::UVector2(CEGUI::UDim(0,50),CEGUI::UDim(0,50)));
    //gJumpBtnWindow->setText("Jump!");
    //CEGUI::System::getSingleton().getGUISheet()->addChildWindow(gJumpBtnWindow);  

    //gJumpBtnWindow->subscribeEvent(CEGUI::PushButton::EventClicked,
    //    CEGUI::Event::Subscriber(&test));

    //CEGUI::Window* myRoot = CEGUI::WindowManager::getSingleton().createWindow( "DefaultWindow", "Root" );
    //CEGUI::System::getSingleton().setGUISheet( myRoot );
    CEGUI::LuaScriptModule *pLua = &CEGUI::LuaScriptModule::create(lua());
    CEGUI::System::getSingleton().setScriptingModule(pLua);
    CEGUI::System::getSingleton().executeScriptFile("main_menu.lua", "lua_scripts");
    //CEGUI::Window *newWindow = CEGUI::WindowManager::getSingleton().loadWindowLayout("main.layout");
    //CEGUI::System::getSingleton().setGUISheet(newWindow);
    //Window *mainWindow = CEGUI::WindowManager::getSingleton().getWindow("Main");
    //Window *mainWindow = CEGUI::System::getSingleton().getGUISheet()->getWindow("Main");
    //mainWindow = mainWindow->getChild("Main");
    //mainWindow->getChild("Main/NewGameConfirm")->hide();
}
void CSTGEngine::destroyScene() {
    destroyLua();
}

//-------------------------------------------------------------------------------------
void CSTGEngine::animate(int time) {

}

//-------------------------------------------------------------------------------------
bool CSTGEngine::frameStarted(const FrameEvent& evt)
{
	// Just a silly example to demonstrate how much easier this is than passing objects to an external framelistener
	headNode->translate(0.0f, 0.000f, 0.0f);
    if (mPlayerInst)
        mPlayerInst->update(evt.timeSinceLastFrame);

    //------------------------------------------------------------------------------
 	return CEngine::frameStarted(evt);
}


//-------------------------------------------------------------------------------------

#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include <fstream>


INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
{
	// Create application object
		CSTGEngine app;

		try
		{
			app.go();
		}
		catch( Ogre::Exception& e )
		{
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
			MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
			std::cerr << "An exception has occured: " << e.getFullDescription().c_str() << std::endl;
#endif
		}

	return 0;
}
